package com;

import java.beans.Beans;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

/**
 * Mysql数据库代码自动生成工具。<br/>
 * 生成成果物如下：
 * <ul>
 * <li>1、**Entity.java类</li>
 * <li>2、**Repository.java接口</li>
 * <li>3、**Mapper.java接口</li>
 * <li>4、**Service.java接口</li>
 * <li>5、**ServiceImpl.java类</li>
 * <li>6、**-mapper.xml文件</li>
 * </ul>
 * 启动参数如下：
 * <ul>
 * <li>1、代码生成路径</li>
 * <li>2、数据库IP</li>
 * <li>3、数据库端口</li>
 * <li>4、数据库Schema</li>
 * <li>5、数据库用户名【请使用管理员用户】</li>
 * <li>6、数据库连接密码</li>
 * </ul>
 *
 * @author Yuqisen
 */
public class MysqlDatabaseExportTool {

  /**
   * 生成路径
   */
  private static String OutputPath = null;
  /**
   * 表名模块映射
   */
  private static Map<String, String> mapper = new HashMap<>();
  /**
   * 表名工程映射
   */
  private static Map<String, String> projects = new HashMap<>();

  /**
   * 无需导出项目
   */
  private static List<String> EXCLUDE_COLUMN = Arrays.asList("ID", "VersionNum", "InsertTime",
      "InsertUser", "UpdateTime", "UpdateUser");

  private static boolean appendDate = true;
  private static SimpleDateFormat JavaDocDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
  
  public static void main(String[] pStr) {
    //档案云:cloud-archives
    mapper.put("Actual", "archives");
    projects.put("Actual", "cloud-archives");
    //预警云:cloud-early-warning
    mapper.put("Alarm", "earlywarning");
    projects.put("Alarm", "cloud-early-warning");
    //感知云:cloud-perception
    mapper.put("Device", "perception");
    projects.put("Device", "cloud-perception");
    //数据维护系统:cloud-maintenance
    mapper.put("Sys", "maintenance");
    projects.put("Sys", "cloud-maintenance");

//    if (pStr.length != Constant.PARAMS_LENGTH) {
//      System.err.println("启动参数错误！");
//      System.err.println("参数：代码生成路径 模块 数据库IP 数据库端口 数据库Schema 数据库用户名【请使用管理员用户】 数据库连接密码");
//      System.err.println("例：D:\\TEMP\\20190330\\Source 192.168.1.7 3306 cppc root admin");
//      return;
//    }
    OutputPath = "C:\\java\\code";
    String host = "localhost";
    String port = "3306";
    String database = "whn";
    String user = "root";
    String password = "123456";
    try {
      Class.forName("com.mysql.jdbc.Driver").newInstance();
      //&tinyInt1isBit=false
      Connection conn = DriverManager
          .getConnection(String.format("jdbc:mysql://%s:%s/information_schema?" +
              "useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull" +
              "&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false" +
              "&user=%s&password=%s", host, port, user, password));
      Statement stmt = conn.createStatement();
      ResultSet tableRet = stmt.executeQuery("select * from tables where table_schema='" + database + "';");
      Connection conn2 = DriverManager
          .getConnection(String.format("jdbc:mysql://%s:%s/%s?" +
              "useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull" +
              "&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false" +
              "&user=%s&password=%s", host, port, database, user, password));
      DatabaseMetaData dbMetaData = conn2.getMetaData();
      // 创建文件夹
      mkdirs();
      // 输出文件
      MysqlDatabaseExportTool tool = new MysqlDatabaseExportTool();
      while (tableRet.next()) {
        TableInfo table = getTableInfo(dbMetaData, tableRet, database);
        if (table != null) {
          System.out.println(String.format("Table: '%s' \t\t : %s", table.getTableName(), table.getRemarks()));

          tool.exportEntity(table);
          tool.exportRepository(table);
          tool.exportMapper(table);
          tool.exportService(table);
          tool.exportServiceImpl(table);
          tool.exportController(table);
          tool.exportMapperXml(table);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println(String.format("\n\nFiles save to '%s'", OutputPath));
  }

  public static TableInfo getTableInfo(DatabaseMetaData dbMetaData, ResultSet table, String schema)
      throws Exception {
    try {
      TableInfo tableInfo = new TableInfo();
      tableInfo.setTableName(table.getString("TABLE_NAME"));
      tableInfo.setRemarks(table.getString("TABLE_COMMENT"));
      ResultSet colRet = dbMetaData.getColumns(schema, "%", tableInfo.getTableName(), "%");
      tableInfo.setCloums(new ArrayList<ColumnInfo>());
      while (colRet.next()) {
        String columnName = colRet.getString("COLUMN_NAME");
        if (EXCLUDE_COLUMN.contains(columnName)) {
          continue;
        }
        String type = "", jdbcType = "";
        String columnType = colRet.getString("TYPE_NAME");
        switch (columnType) {
          case "VARCHAR":
          case "CHAR":
          case "TEXT":
          case "JSON":
            type = "String";
            jdbcType = "VARCHAR";
            break;
          case "DOUBLE":
            type = "Double";
            jdbcType = "DOUBLE";
            break;
          case "DECIMAL":
            type = Constant.FIELD_TYPE_BIGDECIMAL;
            jdbcType = "BIGINT";
            break;
          case "SMALLINT":
          case "INT":
            int datasize = colRet.getInt("COLUMN_SIZE");
            if (datasize > 10) {
              type = "Long";
              jdbcType = "BIGINT";
            } else {
              type = "Integer";
              jdbcType = "INTEGER";
            }
            break;
          case "TINYINT":
          case "BIT":
            type = "boolean";
            jdbcType = "BOOLEAN";
            break;
          case "BIGINT":
            type = "Long";
            jdbcType = "BIGINT";
            break;
          case "TIMESTAMP":
          case "DATETIME":
          case Constant.FIELD_TYPE_DATE:
            type = Constant.FIELD_TYPE_DATE;
            jdbcType = "TIMESTAMP";
            break;
          default:
            throw new Exception("Not expect Type!" + tableInfo.getTableName() + "." + columnType);
        }
        tableInfo.getCloums().add(new ColumnInfo(columnName, type, colRet.getString("REMARKS"), jdbcType));
      }
      return tableInfo;
    } catch (SQLException sql) {
      return null;
    }
  }

  static void mkdirs() {
    File folder = new File(OutputPath);
    folder.deleteOnExit();
    folder.mkdirs();
  }

  /**
   * 导出数据模型类.
   *
   * @param tableInfo 表定义信息
   */
  private void exportEntity(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "domain", tableInfo.getJavaTableName().concat("Entity.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.%s.domain;", tableInfo.getModule())));
    // 引用包
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("import com.cn.ehong.police.affairs.cloud.common.domain.AbstractEntity;"));
    output.write(getLineBytes("import com.cn.ehong.police.affairs.cloud.common.repository.IdGeneratorKey;"));

    output.write(getLineBytes(StringUtils.EMPTY));
    // 附加类型
    Set<String> appendImports = new TreeSet<>();
    tableInfo.getCloums().forEach(column -> {
      if (Constant.FIELD_TYPE_DATE.equals(column.getType())) {
        appendImports.add("import java.util.Date;");
      } else if (Constant.FIELD_TYPE_BIGDECIMAL.equals(column.getType())) {
        appendImports.add("import java.math.BigDecimal;");
      }
    });
    for (String append : appendImports) {
      output.write(getLineBytes(append));
    }
    if (appendImports.size() > 0) {
      output.write(getLineBytes(StringUtils.EMPTY));
    }

    output.write(getLineBytes("import lombok.Data;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("import javax.persistence.Column;"));
    output.write(getLineBytes("import javax.persistence.Entity;"));
    output.write(getLineBytes("import javax.persistence.Table;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks())));
    // 标签
    output.write(getLineBytes("@Data"));
    output.write(getLineBytes("@Entity"));
    output.write(getLineBytes(String.format("@Table(name = \"%s\")", tableInfo.getTableName())));
    output.write(getLineBytes("@IdGeneratorKey"));
    // 类名
    output.write(getLineBytes(String.format("public class %sEntity extends AbstractEntity {", tableInfo.getTableName().replaceAll("_", ""))));
    // 项目
    output.write(getLineBytes(StringUtils.EMPTY));
    tableInfo.getCloums().forEach(column -> {
      try {
        output.write(getLineBytes(String.format("  /** %s */", column.getRemarks())));
        output.write(getLineBytes(String.format("  @Column(name = \"%s\")", column.getName())));
        output.write(getLineBytes(String.format("  %s %s;", column.getType(), column.getFieldName())));
        output.write(getLineBytes(StringUtils.EMPTY));
      } catch (IOException e) {
        e.printStackTrace();
      }
    });
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  /**
   * 导出JPA接口类.
   *
   * @param tableInfo 表定义
   */
  private void exportRepository(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "repository", tableInfo.getJavaTableName().concat("Repository.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.%s.repository;", tableInfo.getModule())));
    // 引用包
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.domain.%sEntity;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes("import com.cn.ehong.police.affairs.cloud.common.repository.BasicCrudRepository;"));
    output.write(getLineBytes("import org.springframework.stereotype.Repository;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks().concat("JPA访问接口."))));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 标签
    output.write(getLineBytes("@Repository"));
    // 类名
    output.write(getLineBytes(String.format("public interface %sRepository extends BasicCrudRepository<%sEntity, String> {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
    // 项目
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  /**
   * 导出Mysql查询接口
   *
   * @param tableInfo 表定义信息
   */
  private void exportMapper(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "mapper", tableInfo.getJavaTableName().concat("Mapper.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.%s.mapper;", tableInfo.getModule())));
    // 引用包
    output.write(getLineBytes("import org.apache.ibatis.annotations.Mapper;"));
    output.write(getLineBytes("import org.apache.ibatis.annotations.Param;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks().concat("Mybatis查询接口."))));
    // 标签
    output.write(getLineBytes("@Mapper"));
    // 类名
    output.write(getLineBytes(String.format("public interface %sMapper {", tableInfo.getJavaTableName())));
    // 项目
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();

  }

  /**
   * 导出业务逻辑接口。
   *
   * @param tableInfo 表定义信息
   */
  private void exportService(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "service", tableInfo.getJavaTableName().concat("Service.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.%s.service;", tableInfo.getModule())));
    // 引用包
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.domain.%sEntity;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("import java.util.List;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks().concat("业务逻辑接口."))));
    // 标签
    // 类名
    output.write(getLineBytes(String.format("public interface %sService {", tableInfo.getJavaTableName())));
    // 项目
    {
      // 查询全部数据
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 查询全部%s数据列表.", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @return %s数据列表", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes(String.format("  List<%sEntity> findAll();", tableInfo.getJavaTableName())));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据登陆
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 提交%s数据.", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @param entity %s对象", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @return 更新后%s对象", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes(String.format("  %sEntity save(%sEntity entity);", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据删除
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 删除%s数据.", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @param id %s数据ID", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes(String.format("  void delete%s(String id);", tableInfo.getJavaTableName())));
    }
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  /**
   * 导出业务逻辑实体类.
   *
   * @param tableInfo 表定义信息
   */
  private void exportServiceImpl(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "service\\impl", tableInfo.getJavaTableName().concat("ServiceImpl.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.%s.service.impl;", tableInfo.getModule())));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 引用包
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.domain.%sEntity;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.mapper.%sMapper;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.repository.%sRepository;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.service.%sService;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes("import org.springframework.beans.factory.annotation.Autowired;"));
    output.write(getLineBytes("import org.springframework.stereotype.Service;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("import java.util.List;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks().concat("业务逻辑类."))));
    // 标签
    output.write(getLineBytes("@Service"));
    // 类名
    output.write(getLineBytes(String.format("public class %sServiceImpl implements %sService {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
    // 项目
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("  @Autowired"));
    output.write(getLineBytes(String.format("  %sRepository repository;", tableInfo.getJavaTableName())));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("  @Autowired"));
    output.write(getLineBytes(String.format("  %sMapper mapper;", tableInfo.getJavaTableName())));
    output.write(getLineBytes(StringUtils.EMPTY));
    {
      // 查询全部数据
      output.write(getLineBytes("  @Override"));
      output.write(getLineBytes(String.format("  public List<%sEntity> findAll() {", tableInfo.getJavaTableName())));
      output.write(getLineBytes("    return repository.findAllByIdIsNotNullOrderByInsertTimeDesc();"));
      output.write(getLineBytes("  }"));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据登陆
      output.write(getLineBytes("  @Override"));
      output.write(getLineBytes(String.format("  public %sEntity save(%sEntity entity) {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
      output.write(getLineBytes("    return repository.save(entity);"));
      output.write(getLineBytes("  }"));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据删除
      output.write(getLineBytes("  @Override"));
      output.write(getLineBytes(String.format("  public void delete%s(String id) {", tableInfo.getJavaTableName())));
      output.write(getLineBytes("    if (repository.existsById(id)) {"));
      output.write(getLineBytes("      repository.deleteById(id);"));
      output.write(getLineBytes("    }"));
      output.write(getLineBytes("  }"));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  /**
   * 导出MysqlXML文件
   *
   * @param tableInfo 表定义信息
   */
  private void exportMapperXml(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "mappers", tableInfo.getJavaTableName().concat("-mapper.xml"));
    // 标签
    output.write(getLineBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
    output.write(getLineBytes("<!DOCTYPE mapper PUBLIC \"-//mapper.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">"));
    output.write(getLineBytes(String.format("<mapper namespace=\"com.cn.ehong.police.affairs.cloud.%s.mapper.%sMapper\">", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(StringUtils.EMPTY));
    // resultMap
    output.write(getLineBytes(String.format("  <!-- %s查询对象 -->", tableInfo.getRemarks())));
    output.write(getLineBytes(String.format("  <resultMap type=\"com.cn.ehong.police.affairs.cloud.%s.domain.%sEntity\" id=\"BasicResultMap\">", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes("    <id column=\"ID\" property=\"id\" jdbcType=\"VARCHAR\" />"));
    tableInfo.getCloums().forEach(column -> {
      try {
        output.write(getLineBytes(String.format("    <result column=\"%s\" property=\"%s\" jdbcType=\"%s\" />", column.getName(), column.getFieldName(), column.getJdbcType())));
      } catch (IOException e) {
        e.printStackTrace();
      }
    });
    output.write(getLineBytes("    <result column=\"VersionNum\" property=\"version\" jdbcType=\"INTEGER\" />"));
    output.write(getLineBytes("    <result column=\"InsertTime\" property=\"insertTime\" jdbcType=\"TIMESTAMP\" />"));
    output.write(getLineBytes("    <result column=\"InsertUser\" property=\"insertUser\" jdbcType=\"VARCHAR\" />"));
    output.write(getLineBytes("    <result column=\"UpdateTime\" property=\"updateTime\" jdbcType=\"TIMESTAMP\" />"));
    output.write(getLineBytes("    <result column=\"UpdateUser\" property=\"updateUser\" jdbcType=\"VARCHAR\" />"));
    output.write(getLineBytes("  </resultMap>"));
    // 结束符
    output.write(getLineBytes("</mapper>"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  /**
   * 导出控制器.
   *
   * @param tableInfo 表定义信息
   */
  private void exportController(TableInfo tableInfo) throws IOException {
    FileOutputStream output = this.openOutputStream(tableInfo.getProject(), tableInfo.getModule(), "controller", tableInfo.getJavaTableName().concat("Controller.java"));
    // 标题
    output.write(getLineBytes(this.getJavaTitle()));
    // package
    output.write(getLineBytes(String.format("package com.cn.ehong.police.affairs.cloud.$s.controller;", tableInfo.getModule())));
    // 引用包
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.domain.%sEntity;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes(String.format("import com.cn.ehong.police.affairs.cloud.%s.service.%sService;", tableInfo.getModule(), tableInfo.getJavaTableName())));
    output.write(getLineBytes("import org.springframework.beans.factory.annotation.Autowired;"));
    output.write(getLineBytes("import org.springframework.http.MediaType;"));
    output.write(getLineBytes("import org.springframework.web.bind.annotation.*;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("import java.util.List;"));
    output.write(getLineBytes(StringUtils.EMPTY));
    // 类注释
    output.write(getLineBytes(this.getJavaClassDoc(tableInfo.getRemarks().concat("控制器."))));
    // 标签
    output.write(getLineBytes(String.format("@RequestMapping(\"/api/%s\")", tableInfo.getJavaTableName())));
    output.write(getLineBytes("@RestController"));
    // 类名
    output.write(getLineBytes(String.format("public class %sController {", tableInfo.getJavaTableName())));
    // 项目
    output.write(getLineBytes(StringUtils.EMPTY));
    output.write(getLineBytes("  @Autowired"));
    output.write(getLineBytes(String.format("  %sService service;", tableInfo.getJavaTableName())));
    output.write(getLineBytes(StringUtils.EMPTY));
    {
      // 查询全部数据
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 查询所有%s列表", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @return 所有%s列表", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes("  @GetMapping(value = \"/all\", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)"));
      output.write(getLineBytes("  @ResponseBody"));
      output.write(getLineBytes(String.format("  public List<%sEntity> findAll%s() {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
      output.write(getLineBytes("    return service.findAll();"));
      output.write(getLineBytes("  }"));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据登陆
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 保存%s", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @param entity %s对象", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @return 保存后%s", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes("  @PostMapping(value = \"/save\", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)"));
      output.write(getLineBytes("  @ResponseBody"));
      output.write(getLineBytes(String.format("  public %sEntity save%sEntity(%sEntity entity) {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
      output.write(getLineBytes("    return service.save(entity);"));
      output.write(getLineBytes("  }"));
      output.write(getLineBytes(StringUtils.EMPTY));
    }
    {
      // 数据删除
      output.write(getLineBytes("  /**"));
      output.write(getLineBytes(String.format("   * 删除%s", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @param id %sID", tableInfo.getRemarks())));
      output.write(getLineBytes(String.format("   * @return 删除后所有%s列表", tableInfo.getRemarks())));
      output.write(getLineBytes("   */"));
      output.write(getLineBytes("  @DeleteMapping(value = \"/delete\", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)"));
      output.write(getLineBytes("  @ResponseBody"));
      output.write(getLineBytes(String.format("  public List<%sEntity> delete%s(@RequestParam String id) {", tableInfo.getJavaTableName(), tableInfo.getJavaTableName())));
      output.write(getLineBytes(String.format("    service.delete%s(id);", tableInfo.getJavaTableName())));
      output.write(getLineBytes(String.format("    return this.findAll%s();", tableInfo.getJavaTableName())));
      output.write(getLineBytes("  }"));
    }
    // 类结束
    output.write(getLineBytes("}"));
    output.write(getLineBytes(StringUtils.EMPTY));
    output.flush();
    output.close();
  }

  private byte[] getLineBytes(String val) throws UnsupportedEncodingException {
    return val.concat("\r\n").getBytes(StandardCharsets.UTF_8);
  }

  /**
   * 打开输出文件流.
   *
   * @param type     文件类型
   * @param fileName 文件名
   * @return 文件流
   * @throws UnsupportedEncodingException 编码异常
   * @throws IOException                  文件异常
   */
  private FileOutputStream openOutputStream(String project, String module, String type, String fileName) throws UnsupportedEncodingException, IOException {
    File folder = null;
    if (Constant.CLASS_TYPE_MAPPER.equals(type)) {
      folder = new File(OutputPath, String.format("%s/src/main/resources/%s", project, type));
    } else {
      folder = new File(OutputPath, String.format("%s/src/main/java/com/cn/ehong/police/affairs/cloud/%s/%s", project, module, type));
    }
    if (!folder.exists()) {
      folder.mkdirs();
    }
    File file = new File(folder, fileName);
    return new FileOutputStream(file);
  }

  /**
   * @return Java类注释
   */
  private String getJavaTitle() {
    StringBuilder builder = new StringBuilder();
    builder.append("/**");
    builder.append("\r\n");
    builder.append(" * @Project: 大数据警务云平台");
    builder.append("\r\n");
    if (appendDate) {
      builder.append(" * @Date: ").append(JavaDocDateFormat.format(new Date()));
      builder.append("\r\n");
    }
    builder.append(" * @Copyright: 2019-2029 www.ehongcn.com Inc. All rights reserved.");
    builder.append("\r\n");
    builder.append(" */");
    return builder.toString();
  }

  /**
   * 生成Java类注释.
   *
   * @param title 类名
   * @return Java类注释
   */
  private String getJavaClassDoc(String title) {
    StringBuilder builder = new StringBuilder();
    builder.append("/**");
    builder.append("\r\n");
    builder.append(String.format(" * %s", title));
    builder.append("\r\n");
    builder.append(" *");
    builder.append("\r\n");
    builder.append(" * @author Ehong");
    builder.append("\r\n");
    if (appendDate) {
      builder.append(" * @since ").append(JavaDocDateFormat.format(new Date()));
      builder.append("\r\n");
    }
    builder.append(" * @version 0.0.1");
    builder.append("\r\n");
    builder.append(" */");
    return builder.toString();
  }


  /**
   * 数据库表对象
   */
  @Setter
  @Getter
  @NoArgsConstructor
  @AllArgsConstructor
  private static class TableInfo {
    private String tableName;
    private String remarks;
    private List<ColumnInfo> cloums = new ArrayList<ColumnInfo>();

    public String getJavaTableName() {
      return this.tableName.replaceAll("_", "");
    }

    public String getProject() {
      int idx = this.tableName.indexOf("_");
      if (idx <= 0) {
        return "cloud-common";
      }
      String project = projects.get(this.tableName.substring(0, idx));
      if (project == null) {
        return "cloud-common";
      }
      return project;
    }

    public String getModule() {
      int idx = this.tableName.indexOf("_");
      if (idx <= 0) {
        return "common";
      }
      String model = mapper.get(this.tableName.substring(0, idx));
      if (model == null) {
        return "common";
      }
      return model;
    }
  }

  /**
   * 数据库列对象
   */
  @Setter
  @Getter
  @NoArgsConstructor
  @AllArgsConstructor
  private static class ColumnInfo {
    private String name;
    private String type;
    private String remarks;
    private String jdbcType;

    public String getFieldName() {
      name = name.substring(0, 1).toLowerCase() + name.substring(1);
      name = name.replaceAll("_", "");
      return name;
    }
  }

  /**
   * 内部常量类.
   */
  private static class Constant {

    /** 参数个数 */
    static final int PARAMS_LENGTH = 6;

    /** 项目类型：日期 */
    static final String FIELD_TYPE_DATE = "Date";

    /** 项目类型：数值 */
    static final String FIELD_TYPE_BIGDECIMAL = "BigDecimal";

    /** 文件类型：Mapper */
    static final String CLASS_TYPE_MAPPER = "mappers";
  }

}
